
#include "nane/CodecSystem/Sound/OGG/OGGSoundDecoder.h"
#include "nane/core/assert.h"

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static size_t s_readOgg( void* _ptr, size_t _size, size_t _nmemb, void* _datasource )
    {
        IInputRef* stream = static_cast<IInputRef*>(_datasource);
        uint32 bytesRead = stream->Read(static_cast<byte*>(_ptr), static_cast<uint32>(_size * _nmemb));
        return static_cast<size_t>(bytesRead);
    }    
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static size_t s_readSeekableOgg( void* _ptr, size_t _size, size_t _nmemb, void* _datasource )
    {
        IInputSeekableRef* stream = static_cast<IInputSeekableRef*>(_datasource);
        uint32 bytesRead = stream->Read(static_cast<byte*>(_ptr), static_cast<uint32>(_size * _nmemb));
        return static_cast<size_t>(bytesRead);        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static int s_seekOgg( void* _datasource, ogg_int64_t _offset, int _whence )
    {
        IInputSeekableRef* stream = static_cast<IInputSeekableRef*>(_datasource);
        uint32 offset = static_cast<uint32>(_offset);
        switch( _whence )
        {
            case SEEK_SET:
                break;
            case SEEK_CUR:
                offset += stream->Tell();
                break;
            case SEEK_END:
                offset += stream->Size();
                break;
        }
        stream->Seek(offset);
        return 0;
    }    
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static long s_tellOgg( void *_datasource )
    {
        IInputSeekableRef* stream = static_cast<IInputSeekableRef*>( _datasource );
        return static_cast<long>(stream->Tell());
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    OGGSoundDecoder::OGGSoundDecoder()
        : m_dataSize( 0 )
        , m_channels( 0 )
        , m_frequency( 0 )
        , m_bitsPerSample( 0 )
        , m_bytesRead( 0 )
    {
        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    OGGSoundDecoder::~OGGSoundDecoder()
    {
        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool OGGSoundDecoder::Initialize( IInputRef* _input )
    {
        ov_callbacks vorbisCallbacks;
        vorbisCallbacks.read_func = &s_readOgg;
        vorbisCallbacks.seek_func = NULL;
        vorbisCallbacks.tell_func = NULL;
        vorbisCallbacks.close_func = NULL;

        if( ov_open_callbacks(_input, &m_oggVorbisFile, NULL, 0, vorbisCallbacks) < 0 )
        {
            return false;
        }
        
        m_bitsPerSample = 16;   // ???
        vorbis_info* vorbisInfo = ov_info(&m_oggVorbisFile, -1);
        m_frequency = static_cast<uint32>(vorbisInfo->rate);
        m_channels = static_cast<uint16>(vorbisInfo->channels);
        m_dataSize = 0;         // not seekable
        
        m_inputPtr = InputPtr(_input);
        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool OGGSoundDecoder::Initialize( IInputSeekableRef* _inputSeekable )
    {
        ov_callbacks vorbisCallbacks;
        vorbisCallbacks.read_func = &s_readSeekableOgg;
        vorbisCallbacks.seek_func = &s_seekOgg;
        vorbisCallbacks.tell_func = &s_tellOgg;
        vorbisCallbacks.close_func = NULL;

        if( ov_open_callbacks(_inputSeekable, &m_oggVorbisFile, NULL, 0, vorbisCallbacks) < 0 )
        {
            return false;
        }
        
        m_bitsPerSample = 16;   // ???
        vorbis_info* vorbisInfo = ov_info(&m_oggVorbisFile, -1);
        m_frequency = static_cast<uint32>(vorbisInfo->rate);
        m_channels = static_cast<uint16>(vorbisInfo->channels);
        
        ogg_int64_t pcmTotal = ov_pcm_total(&m_oggVorbisFile, -1);
        m_dataSize = static_cast<uint32>(pcmTotal * m_bitsPerSample / 8);
        
        m_inputPtr = InputPtr(_inputSeekable);
        return true;        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void OGGSoundDecoder::Shutdown()
    {
        ov_clear(&m_oggVorbisFile);
        m_inputPtr = NullPtr;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    uint32 OGGSoundDecoder::GetFrequency()
    {
        return m_frequency;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    uint16 OGGSoundDecoder::GetChannels()
    {
        return m_channels;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    uint16 OGGSoundDecoder::GetBitsPerSample()
    {
        return m_bitsPerSample;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    uint32 OGGSoundDecoder::GetLengthInBytes()
    {
        return m_dataSize;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    uint32 OGGSoundDecoder::Read( byte* _buffer, uint32 _count )
    {
        int current_section;
        long decodeSize = 0;

        unsigned long bytesDone = 0;

        while( (decodeSize = ov_read(&m_oggVorbisFile, reinterpret_cast<char*>(_buffer) + bytesDone, _count - bytesDone, 0, 2, 1, &current_section)) )
        {
            bytesDone += decodeSize;
 	
            NANE_ASSERT( bytesDone <= _count, "Buffer overflow check" );
            if( bytesDone >= _count )
            {
                break;
            }
        }

        return bytesDone;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
